//
//  XTRefreshControl.swift
//  MyBlog
//
//  Created by victor on 17/4/5.
//  Copyright © 2017年 victor. All rights reserved.
//

import UIKit

/// 刷新状态切换的临界点
private let XTRefreshOffset: CGFloat = 126

/// 刷新状态
///
/// - Normal:      普通状态，什么都不做
/// - Pulling:     超过临界点，如果放手，开始刷新
/// - WillRefresh: 用户超过临界点，并且放手
enum XTRefreshState {
    case Normal
    case Pulling
    case WillRefresh
}

/// 刷新控件 - 负责刷新相关的逻辑处理
class XTRefreshControl: UIControl {

    // MARK: - 属性
    /// 刷新控件的父视图，下拉刷新控件应该适用于 UITableView / UICollectionView
    fileprivate weak var scrollView: UIScrollView?
    fileprivate lazy var refreshView: XTRefreshView = XTRefreshView()
    
    // MARK: - 构造函数
    init() {
        super.init(frame: CGRect())
        
        setupUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    /**
     willMove这个方法 用addSubview 方法时会调用
     - 当添加到父视图的时候，newSuperview 是父视图
     - 当父视图被移除，newSuperview 是 nil,没有父视图引用了，自身也就被释放了
     */
    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        
        // 判断父视图的类型，如果父视图不是UIScrollView 直接返回
        guard let sv = newSuperview as? UIScrollView else {
            return
        }
        
        // 记录父视图
        scrollView = sv
        
        // KVO 监听父视图的 contentOffset
        // 在程序中，通常只监听某一个对象的某几个属性，如果属性太多，方法会很乱
        scrollView?.addObserver(self, forKeyPath: "contentoffset", options: [], context: nil)
    }
    
    // 本视图从父视图上移除
    // 提示：所有的下拉刷新框架都是监听父视图的 contentOffset
    // 所有的框架的 KVO 监听实现思路都是这个！
    override func removeFromSuperview() {
        
        // 调用移除的时候要在前面来调用，因为这个时候superView 还存在
        superview?.removeObserver(self, forKeyPath: "contentoffset") 
        
        super.removeFromSuperview()
        // superView 不存在
    }
    
    // 所有 KVO 方法会统一调用此方法
    // 在程序中，通常只监听某一个对象的某几个属性，如果属性太多，方法会很乱！
    // 观察者模式，在不需要的时候，都需要释放
    // - 通知中心：如果不释放，什么也不会发生，但是会有内存泄漏，会有多次注册的可能！
    // - KVO：如果不释放，会崩溃！
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        // contentOffset 的 y 值跟 contentInset 的 top 有关
        
        guard let sv = scrollView else {
            return
        }
        
        // 初始高度就应该是 0 前面加个'-'是为了取反
        let height = -(sv.contentInset.top + sv.contentOffset.y)
        
        // 必须要把0守护住
        if height < 0 {
            return
        }
        
        // 可以根据高度设置刷新控件的 frame
        self.frame = CGRect(x: 0,
                            y: -height,
                            width: sv.bounds.width,
                            height: height)
        
        // --- 传递父视图高度，如果正在刷新中，不传递
        // --- 把代码放在`最合适`的位置！
        
        print(height)
        // 判断临界点 - 只需要判断一次
        if sv.isDragging {
            // 超过边界，同时状态还是普通，就将状态改为下拉
            if height > XTRefreshOffset && (refreshView.refreshState == .Normal) {
                print("放手刷新")
                refreshView.refreshState = .Pulling
            } else if height <= XTRefreshOffset && (refreshView.refreshState == .Pulling) {
                // 小于等于边界，且是正在下拉的状态中
                print("再使劲")
                refreshView.refreshState = .Normal
            }
        } else {
            // 放手 - 判断是否超过临界点
            if refreshView.refreshState == .Pulling {
                print("准备开始刷新")
                
//                // 刷新结束之后，将 状态修改为 .normal 才能够继续响应刷新
//                refreshView.refreshState = .WillRefresh
//                
//                // 让整个刷新视图能够显示出来
//                // 解决方法: 修改表格的contentInset
//                var inset = sv.contentInset
//                inset.top += XTRefreshOffset
//                
//                sv.contentInset = inset
                
                // 调用开始刷新方法
                beginRefreshing()
                
                // 发送刷新数据事件
                sendActions(for: .valueChanged)
            }
        }
        
    }
    
    /// 开始刷新
    func beginRefreshing() {
        
        print("开始刷新")
        
        guard let sv = scrollView else {
            return
        }
        
        // 判断是否正在刷新，如果正在刷新，直接返回
        if refreshView.refreshState == .WillRefresh {
            return
        }
        
        // 设置刷新状态 让小菊花显示出来
        refreshView.refreshState = .WillRefresh
        
        // 设置间距
        var inset = sv.contentInset
        inset.top += XTRefreshOffset
        
        // 会触发表格视图的 contentoffset 变化
        sv.contentInset = inset
        
        // 如果开始调用beginRefreshing，会重复发送刷新事件
//        sendActions(for: .valueChanged)
    }
    
    /// 结束刷新
    func endRefreshing() {
        
        print("结束刷新")
        
        guard let sv = scrollView else {
            return
        }
        
        // 判断状态，是否正在刷新，如果不是，直接返回
        if refreshView.refreshState != .WillRefresh {
            return
        }
        
        // 恢复刷新视图的状态
        refreshView.refreshState = .Normal
        
        // 恢复表格视图的contentInset
        var inset = sv.contentInset
        inset.top -= XTRefreshOffset
        
        sv.contentInset = inset
    }

}

extension XTRefreshControl {
    fileprivate func setupUI() {
        backgroundColor = superview?.backgroundColor
        
        // 设置超出边界不显示
        //clipsToBounds = true

        // 添加刷新视图
        addSubview(refreshView)

        // 自动布局
        refreshView.translatesAutoresizingMaskIntoConstraints = false

        refreshView.snp.makeConstraints { (make) in
            make.centerX.equalTo(self.snp.centerX)
            make.bottom.equalTo(self.snp.bottom)
            make.width.equalTo(158)
            make.height.equalTo(60)
        }
    }
}
